package com.yswee.framework.net.rbd;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import org.apache.http.protocol.HTTP;

import com.yswee.framework.Application;
import com.yswee.framework.net.Entity;
import com.yswee.framework.net.ITask;
import com.yswee.framework.net.engine.IEngineTask;
import com.yswee.framework.net.engine.ThreadPoolExecutor;
import com.yswee.framework.net.http.Params;
import com.yswee.framework.utils.FileUtils;
import com.yswee.framework.utils.HttpUtils;
import com.yswee.framework.utils.LogcatUtils;
import com.yswee.framework.utils.StringUtils;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;

class Task implements IEngineTask, ITask {
	
	private static final String TAG = Task.class.getSimpleName();
	
	private static final String TEMPFILE_EXT = ".tmp";
	private static final int BUFFER_COUNT = 5;
	private static final int BUFFER_SIZE = 1024 * 8;
	private static final int MAXRETRYTIME = 3;
	
	private int currRetryTime = 0;
	protected boolean isCanceled = false;
    
	protected RBDEntity mEntity;
	protected RBDCallback mListener;
	
	Handler mHandler = new Handler(Looper.getMainLooper()) {
		@Override
		public void handleMessage(Message msg) {
			int what = msg.what;
			switch (what) {
			case RBDEntity.DOWNLOADPROGRESS:
				mEntity.setEntityStatus(what);
				mEntity.setDownloadProgress(msg.arg1, msg.arg2);
				if (mListener != null)
					mListener.onDownloadProgress(mEntity);
				break;
			case RBDEntity.FINISHED:
				mEntity.setEntityStatus(what);
				if (mListener != null)
					mListener.onFinish(mEntity);
				unregistTask();
				break;
			case RBDEntity.ERROR:
				if(HttpUtils.isNetWorkConnected(Application.get())) {
					mEntity.setEntityStatus(what);
				} else {
					mEntity.setEntityStatus(Entity.NETINVALID);
				}
				if (mListener != null) {
					mListener.onError(mEntity);
				}
				unregistTask();
				break;
			case Entity.NEEDLOGIN:
				mEntity.setEntityStatus(what);
				if (mListener != null) {
					mListener.onError(mEntity);
				}
				unregistTask();
				break;
			case RBDEntity.CENCELED:
				mEntity.setEntityStatus(what);
				if (mListener != null)
					mListener.onCancel(mEntity);
				unregistTask();
				break;
			case RBDEntity.INVALID:
				mEntity.setEntityStatus(what);
				if (mListener != null)
					mListener.onInvalid(mEntity);
				break;
			default:
				break;
			}
		};
	};
	
	Task (RBDEntity entity, RBDCallback listener) {
		mEntity = entity;
		mListener = listener;
	}

	@Override
	public int getPriority() {
		if(mEntity != null) {
			return mEntity.getPriority();
		}
		return IEngineTask.DEFAULTPRIORITY;
	}

	@Override
	public boolean isValid() {
		if(isCanceled)
			return false;
		if(mEntity != null) {
			boolean valid = mEntity.isValid();
			if(!valid) {
				mHandler.sendEmptyMessage(RBDEntity.INVALID);
			}
			return valid;
		}
		return true;
	}

	@Override
	public void notifyWake() {
		
	}

	@Override
	public void excute() {
		if(mEntity != null) {
			ThreadPoolExecutor.get().execute(this);
			registTask();
		}
	}

	protected void registTask() {
		if(mEntity.getContext() != null) {
			mEntity.getContext().registTask(this);
		}
	}
	
	protected void unregistTask() {
		if(mEntity.getContext() != null) {
			mEntity.getContext().unregistTask(this);
		}
	}

	@Override
	public void cancel() {
		isCanceled = true;
		ThreadPoolExecutor.get().remove(this);
		LogcatUtils.i(TAG, "cancel task url : " + mEntity.getBaseUrl());
	}

	@Override
	public void run() {
		try {
			if(TextUtils.isEmpty(mEntity.getBaseUrl())) {
				mEntity.setMessage("base url is empty");
	        	mHandler.sendEmptyMessage(RBDEntity.ERROR);
	        	return;
			}
			if(TextUtils.isEmpty(mEntity.getFilePath())) {
				mEntity.setMessage("saved file path is empty");
	        	mHandler.sendEmptyMessage(RBDEntity.ERROR);
	        	return;
			}
			if (isCanceled) {
                mHandler.sendEmptyMessage(RBDEntity.CENCELED);
                return;
            }
            long startTime = System.currentTimeMillis();
            currRetryTime = 0;
            excuteHttpRequest();
            LogcatUtils.i(TAG, "execute task finish time is: " + (System.currentTimeMillis() - startTime) + "ms");
        } catch (OutOfMemoryError e) {
        	LogcatUtils.e(TAG, e);
        }
	}
	
	private void excuteHttpRequest() {
        String errormsg = "";
		if(currRetryTime++ >= MAXRETRYTIME) {
			errormsg = "connection has runout max retry time";
		} else {
        	if(mListener != null)
        		mListener.onBeginRequest(mEntity);
        	HttpURLConnection conn = null;
	        try {
	            String baseUrl = mEntity.getBaseUrl();
	            if (baseUrl == null || "".equals(baseUrl)) {
	            	errormsg = "connection url is empty";
	            } else {
		            LogcatUtils.i(TAG, "create connection baseUrl:" + baseUrl);
		            if (HttpUtils.isNetWorkConnected(Application.get())) {
		            	conn = initHttpConnection();
			            int code = 0;
			            try {
			                if (isCanceled) {
			                    mHandler.sendEmptyMessage(RBDEntity.CENCELED);
			                    return;
			                }
			                conn.connect();
			                code = conn.getResponseCode();
			            } catch (SocketTimeoutException e) {
			            	errormsg = "SocketTimeoutException : " + e.getMessage();
			            } catch (UnknownHostException e) {
			            	errormsg = "UnknownHostException : " + e.getMessage();
			            } catch (Exception e) {
			            	errormsg = "Exception : " + e.getMessage();
			            }
			            LogcatUtils.i(TAG, "response code=" + code);
			            // 201 或 206 代表成功请求
			            if (code == HttpURLConnection.HTTP_OK || 
			            	code == HttpURLConnection.HTTP_PARTIAL) {
			            	excuteHttpResponse(conn);
			            // 301 302重定向
			            } else if (code == HttpURLConnection.HTTP_MOVED_TEMP ||
			            		code == HttpURLConnection.HTTP_MOVED_PERM) {
			            	String newurl = conn.getHeaderField("location");
			            	if (!StringUtils.isEmpty(newurl)) {
//			            		if (newurl.toLowerCase().contains(ApiConstant.API_SYSTEM_LOGIN.toLowerCase())) {
//				                	mEntity.setMessage(errormsg);
//				                	mHandler.sendEmptyMessage(Entity.NEEDLOGIN);
//			            		} else {
				            		mEntity.setBaseUrl(newurl);
				            		excuteHttpRequest();
//			            		}
			            		return;
			            	} else {
			            		errormsg = "new url is empty, response code:" + code;
			            	}
			            // 500 -- 505 服务器错误，重试http请求
			            } else if (mEntity.isRetryOnError() && (code < 0 || 
			               (code >= HttpURLConnection.HTTP_INTERNAL_ERROR && 
			            	code <= HttpURLConnection.HTTP_VERSION))) {
			                LogcatUtils.e(TAG, "retry connect, prev response code:" + code);
			                destroyHttpConnection(conn);
			                conn = null;
	                        Thread.sleep(500);
			                excuteHttpRequest();
			            } else {
			            	errormsg = "No Exception, response code:" + code;
			            }
		            } else {
		            	errormsg = "network not available";
		            }
	            }
	        } catch (Exception e) {
	        	errormsg = e.getMessage();
	        } finally {
	            destroyHttpConnection(conn);
	        }
		}
        if(!TextUtils.isEmpty(errormsg)) {
        	mEntity.setMessage(errormsg);
        	mHandler.sendEmptyMessage(RBDEntity.ERROR);
        	LogcatUtils.e(TAG, errormsg);
        }
    }

	private HttpURLConnection initHttpConnection() {
		HttpURLConnection conn = null;
        try {
        	HttpUtils.trustAllHosts();
        	long filesize = FileUtils.getFileLength(mEntity.getFilePath() + TEMPFILE_EXT);
        	conn = HttpUtils.createHttpConnection(mEntity.getBaseUrl());
		    conn.setConnectTimeout(Params.HTTPGET_CONN_TIME_OUT_LONG);
		    conn.setReadTimeout(Params.HTTPGET_READ_TIME_OUT_LONG);
		    conn.setRequestProperty(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE);
			conn.setRequestProperty(HTTP.CONTENT_TYPE, HTTP.OCTET_STREAM_TYPE);
			conn.setRequestProperty("Range", "bytes=" + filesize + "-");
			conn.setRequestMethod("GET");
        	if(mListener != null)
        		mListener.onBeginConnection(conn);
		} catch (Exception e) {
			LogcatUtils.e(TAG, e);
		}
        return conn;
	}

	private void excuteHttpResponse(HttpURLConnection conn) {
		String errormsg = "";
        try {
        	if(mListener != null)
        		mListener.onBeginResponse(conn);
        	errormsg = getStreamData(conn);
            if (isCanceled) {
            	mEntity.setMessage(errormsg);
            	mHandler.sendEmptyMessage(RBDEntity.CENCELED);
            	return;
            }
        } catch (Exception e) {
        	errormsg = e.getMessage();
        } finally {
        }
    	if(mListener != null)
    		mListener.onEndResponse(mEntity);
        if(!TextUtils.isEmpty(errormsg)) {
        	mEntity.setMessage(errormsg);
        	mHandler.sendEmptyMessage(RBDEntity.ERROR);
        	LogcatUtils.e(TAG, errormsg);
        } else {
        	mHandler.sendEmptyMessage(RBDEntity.FINISHED);
        }
    }
	
	private String getStreamData(HttpURLConnection connection) throws Exception {
		InputStream stream = null;
		RandomAccessFile accessFile = null;
        byte[] buffer = new byte[BUFFER_SIZE];
		try {
			int oldsize = 0;
	    	int totalsize = 0;
	    	
			ResponseRange res_range = getResponseContentRange(connection);
			if(res_range != null && res_range.start > 0 && res_range.length > 0) {
				oldsize = res_range.start;
				totalsize = res_range.length - res_range.start;
			} else {
				oldsize = 0;
				totalsize = connection.getContentLength();
			}
	    	if(totalsize <= 0) {
	    		return "connection getContentLength <= 0";
	    	}
			File file = new File(mEntity.getFilePath() + TEMPFILE_EXT);
	        if (!file.exists()) {
	        	try {
	        		String folderPath = mEntity.getFilePath().substring(0, mEntity.getFilePath().lastIndexOf(File.separator));
	        		File folder = new File(folderPath);
	        		if(!folder.exists())
	        			folder.mkdirs(); 
	            } catch (Exception e) {
	            }
	            file.createNewFile();
	        }
	        setProgress(oldsize + 0, oldsize + totalsize);
	        accessFile = new RandomAccessFile(file, "rwd");
	        accessFile.seek(oldsize);
	        stream = connection.getInputStream();
	        
	        int readLen = -1, recvsize = 0, blockNum = 0;
	        while ((readLen = stream.read(buffer)) != -1) {
	            recvsize += readLen;
	        	accessFile.write(buffer, 0, readLen);
	            // 更新进度
	            if (blockNum % BUFFER_COUNT == 0)
	            	setProgress(oldsize + recvsize, oldsize + totalsize);
	            blockNum++;
	        	// 判断是否用户取消下载
	            if (isCanceled || recvsize >= totalsize) {
	                connection.disconnect();
	                break;
	            }
	        }
        	setProgress(oldsize + recvsize, oldsize + totalsize);
	        if(recvsize >= totalsize) {
	        	File newfile = new File(mEntity.getFilePath());
	        	if(newfile.exists())
	        		newfile.delete();
	        	file.renameTo(newfile);
	        	return "";
	        } else {
	        	if(isCanceled)
	        		return "user canceled download";
	        	else
	        		return "connection recv size < total size";
	        }
		} catch (Exception e) {
			LogcatUtils.e(TAG, e);
			return e.getMessage();
		} finally {
	        try {
	            buffer = null;
	            if (stream != null) {
	                stream.close();
	                stream = null;
	            }
	            if(accessFile != null) {
	            	accessFile.close();
	            	accessFile = null;
	            }
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
		}
	}
	
	private ResponseRange getResponseContentRange(HttpURLConnection connection) {
		for (int j = 0;; j++) {
			String headerkey = connection.getHeaderFieldKey(j);
			if (headerkey != null) {
				if (headerkey.toLowerCase().equals("content-range")) {
					String range = connection.getHeaderField(headerkey);
					int i = "bytes ".length();
					if (i > range.length()) {
						// content-range 返回不合法时,或者 不支持断点续传， 为0 跳出
						break;
					}
					String str = null;
					try {
						str = range.substring(i, range.length());
					} catch (IndexOutOfBoundsException e) {
						// content-range 返回不合法时,或者 不支持断点续传， 为0 跳出
						e.printStackTrace();
						break;
					}
					String[] split = str.split("-");
					if (split != null && split.length == 2) {
						try {
							ResponseRange r_range = new ResponseRange();
							r_range.start = Integer.parseInt(split[0].trim());
							String[] len = split[1].split("/");
							r_range.length = Integer.parseInt(len[1].trim());
							return r_range;
						} catch (Exception e) {
							// content-range 返回不合法时,或者 不支持断点续传， 为0 跳出
							e.printStackTrace();
							break;
						}
					}
					break;
				}
			} else {
				break;
			}
		}
		return null;
	}
	
	private class ResponseRange {
		public int start;
		public int length;
	}

	private void setProgress(int current, int total) {
		Message msg = Message.obtain();
        msg.what = RBDEntity.DOWNLOADPROGRESS;
        msg.arg1 = current;
        msg.arg2 = total;
        mHandler.sendMessage(msg);
	}
	
	private void destroyHttpConnection(HttpURLConnection conn) {
        if (conn != null) {
            try {
                conn.disconnect();
            } catch (Exception e) {
            	LogcatUtils.e(TAG, e);
            }
            conn = null;
        }
    }
}